#include "pch.h"
#include <memory>
#include "ImportDataThread.h"
#include "ImportData.h"
#include "../Base/Base.h"

ImportDataThread::ImportDataThread(ImportData *import_data, DatabaseConfig &hana_config, std::shared_ptr<EnclaveProvider> enclave_provider, Config &config)
    : import_data_(import_data), enclave_provider_(enclave_provider)
	, hana_data_(hana_config), config_(config)
	, field_values(20480 * config_.read_count_, 60 * config_.read_count_)
	, encrypt_field_values(20480 * config_.read_count_, 60 * config_.read_count_)
	, wirte_field_values(20480 * config_.read_count_, 60 * config_.read_count_)
{
}


ImportDataThread::~ImportDataThread()
{
}

bool ImportDataThread::runLoop()
{
	TableConfig *table_config = nullptr;
	std::vector<Field> *table_columns = nullptr;
	std::vector<Field> select_columns;
	ImportDataCache::Data pos;
	int num_rows = 0;
	field_values.clear();

	encrypt_field_values.clear();
	wirte_field_values.clear();

    if (import_data_->getTableFields(&table_config, &table_columns, select_columns, field_values, num_rows, pos) == false)
    {
		LOG(ERROR) << "getTableFields error";
        return false;
    }

	if (num_rows == 0)
	{
		return true;
	}

	LOG(INFO) << "Import data, table = " << table_config->name_ << ", thread id = " << thread_id_;

	int num_fields = (int)table_columns->size();

	int buf_len = 1024;
	std::unique_ptr<char> buf = std::unique_ptr<char>(new char[buf_len]);

	LOG(INFO) << "Encrypt datas begin ...";

	const int max_encrypt_num_rows = 50;
	const int max_encrypt_buf_len = 6000 * max_encrypt_num_rows;
	int encrypt_num_rows = num_rows > max_encrypt_num_rows ? max_encrypt_num_rows : num_rows;
	int total_encrypt_num_rows = 0;

	std::unique_ptr<int> values_type = std::unique_ptr<int>(new int[max_encrypt_num_rows * num_fields]);

	int select_columns_size = (int)select_columns.size();

	field_values.resetPos();

	while (total_encrypt_num_rows != num_rows)
	{
		int values_count = select_columns_size * encrypt_num_rows;
		int values_size = 0;

		memset(values_type.get(), 0, sizeof(int) * max_encrypt_num_rows * num_fields);

		for (int i = 0; i < encrypt_num_rows; i++)
		{
			for (int j = 0; j < select_columns_size; j++)
			{
				int index = i * select_columns_size + j;

				values_size += field_values.field_len(field_values.field_index_ + index);
				if ((*table_columns)[j].type_ == "DECIMAL")
				{
					values_type.get()[index] = 1;
				}
				else
				{
					values_type.get()[index] = 0;
				}
			}
		}

		int encrypt_buf_size = encrypt_field_values.buf_size() - encrypt_field_values.buf_pos_;
		encrypt_buf_size = encrypt_buf_size > max_encrypt_buf_len ? max_encrypt_buf_len : encrypt_buf_size;

		int len = enclave_provider_->encryptex(field_values.buf() + field_values.buf_pos_, values_size, field_values.field_lens() + field_values.field_index_,
			values_type.get(), 
			values_count,
			encrypt_field_values.buf() + encrypt_field_values.buf_pos_,
			encrypt_buf_size,
			encrypt_field_values.field_lens() + encrypt_field_values.field_index_);

		if (len == 0)
		{
			LOG(ERROR) << "Encrypt error";
			return true;
		}

		field_values.buf_pos_ += values_size;
		field_values.field_index_ += values_count;

		encrypt_field_values.buf_pos_ += len;
		encrypt_field_values.field_index_ += values_count;

		total_encrypt_num_rows += encrypt_num_rows;

		encrypt_num_rows = num_rows - total_encrypt_num_rows > max_encrypt_num_rows ? max_encrypt_num_rows : num_rows - total_encrypt_num_rows;
	}

	field_values.resetPos();

	encrypt_field_values.resetPos();

	for (int i = 0; i < num_rows; ++i)
	{
		int row_pos = field_values.buf_pos_;

		for (size_t j = 0; j < select_columns_size; ++j)
		{
			int index = (int)(i * select_columns_size + j);

			int len = field_values.field_len(index);
			int encrypt_len = encrypt_field_values.field_len(index);

			if (len == 0)
			{
				wirte_field_values.add_field(nullptr, 0);
			}
			else if ((*table_columns)[j].type_ == "DECIMAL")
			{
				wirte_field_values.add_field(field_values.buf() + field_values.buf_pos_, len);
			}
			else
			{
				wirte_field_values.add_field(encrypt_field_values.buf() + encrypt_field_values.buf_pos_, encrypt_len);
			}

			field_values.buf_pos_ += len;
			++field_values.field_index_;

			encrypt_field_values.buf_pos_ += encrypt_len;
			++encrypt_field_values.field_index_;
		}

		for (auto column_iter = import_data_->columns_ex_.begin(); column_iter != import_data_->columns_ex_.end(); ++column_iter)
		{
			if (column_iter->name_ == "VAULT_ID")
			{
				wirte_field_values.add_field(enclave_provider_->id_.c_str(), (int)enclave_provider_->id_.length());
			}
			else if (column_iter->name_ == "SIGN_VAULT_ID")
			{
				wirte_field_values.add_field(enclave_provider_->id_.c_str(), (int)enclave_provider_->id_.length());
			}
			else if (column_iter->name_ == "VALUES_HASH")
			{
				memset(buf.get(), 0, buf_len);
				int sha256_len = enclave_provider_->sha256_encrytp(field_values.buf() + row_pos, field_values.buf_pos_ - row_pos, buf.get(), buf_len);

				if (sha256_len == 0)
				{
					LOG(ERROR) << "sha256_encrytp error";
					return true;
				}

				wirte_field_values.add_field(buf.get(), sha256_len);
			}
		}
	}

	LOG(INFO) << "Encrypt datas end ...";

	LOG(INFO) << "Insert datas to hana begin ...";
	auto temp_columns = *table_columns;
	temp_columns.erase(temp_columns.begin() + temp_columns.size() - 1);
	bool inser_result = hana_data_.insert(table_config->name_, wirte_field_values, num_rows, temp_columns);
	LOG(INFO) << "Insert datas to hana end ...";

	if (inser_result == false)
	{
		LOG(ERROR) << "Insert error";
		return true;
	}

	import_data_->importProgress(this, *table_config, num_rows);

	wirte_field_values.clear();

	encrypt_field_values.clear();
	field_values.clear();

	import_data_->removeFaild(table_config->name_.c_str(), pos);

    return true;
}
